﻿using ExcelFileMonitor.NPOI.Config;
using ExcelFileMonitor.NPOI.Models;
using ExcelFileMonitor.NPOI.Utilities;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;

namespace ExcelFileMonitor.NPOI.Services
{
    public class ExcelFileWatcher : IExcelFileWatcher
    {
        private FileSystemWatcher _fileWatcher;
        private readonly ILogger<ExcelFileWatcher> _logger;
        private readonly FileWatcherConfig _config;
        private readonly FileAccessHelper _fileAccessHelper;
        private readonly ConcurrentDictionary<string, DateTime> _fileLastSeen;
        private readonly ConcurrentDictionary<string, DateTime> _pendingChanges;
        private readonly Timer _debounceTimer;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private readonly SemaphoreSlim _processingSemaphore;

        private int _activeProcesses = 0;
        private int _totalFilesDetected = 0;
        private bool _isWatching = false;
        private readonly object _watchLock = new object();

        public event EventHandler<ExcelFileEvent> FileDetected;
        public event EventHandler<Exception> ErrorOccurred;
        public event EventHandler<string> StatusChanged;

        public string RootMonitorPath => _config.RootMonitorPath;
        public bool IsWatching => _isWatching;
        public int ActiveProcesses => _activeProcesses;
        public int TotalFilesDetected => _totalFilesDetected;
        public IReadOnlyCollection<string> CurrentFiles => _fileLastSeen.Keys.ToList().AsReadOnly();

        public ExcelFileWatcher(FileWatcherConfig config, ILogger<ExcelFileWatcher> logger = null)
        {
            _config = config ?? throw new ArgumentNullException(nameof(config));
            _config.Validate();

            _logger = logger;
            _fileAccessHelper = new FileAccessHelper(_config.RetryAttempts, _config.RetryDelayMs);
            _fileLastSeen = new ConcurrentDictionary<string, DateTime>();
            _pendingChanges = new ConcurrentDictionary<string, DateTime>();
            _cancellationTokenSource = new CancellationTokenSource();
            _processingSemaphore = new SemaphoreSlim(_config.MaxConcurrentProcesses, _config.MaxConcurrentProcesses);

            _debounceTimer = new Timer(DebounceTimerCallback, null, Timeout.Infinite, Timeout.Infinite);

            InitializeFileWatcher();
        }

        public async Task StartWatchingAsync()
        {
            lock (_watchLock)
            {
                if (_isWatching)
                {
                    _logger?.LogWarning("文件监控已经在运行中");
                    return;
                }
                _isWatching = true;
            }

            try
            {
                _fileWatcher.EnableRaisingEvents = true;

                // 扫描现有文件
                await ScanExistingFilesAsync();

                var message = $"Excel文件监控已启动，监控路径: {_config.RootMonitorPath}";
                _logger?.LogInformation(message);
                StatusChanged?.Invoke(this, message);
            }
            catch (Exception ex)
            {
                _isWatching = false;
                _logger?.LogError(ex, "启动文件监控失败");
                OnErrorOccurred(ex);
                throw;
            }
        }

        public async Task StopWatchingAsync()
        {
            lock (_watchLock)
            {
                if (!_isWatching)
                {
                    _logger?.LogWarning("文件监控未在运行中");
                    return;
                }
                _isWatching = false;
            }

            try
            {
                _fileWatcher.EnableRaisingEvents = false;
                _cancellationTokenSource.Cancel();

                var message = "Excel文件监控已停止";
                _logger?.LogInformation(message);
                StatusChanged?.Invoke(this, message);
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "停止文件监控失败");
                OnErrorOccurred(ex);
                throw;
            }
        }

        public async Task ScanExistingFilesAsync()
        {
            try
            {
                _logger?.LogInformation("开始扫描现有Excel文件...");
                StatusChanged?.Invoke(this, "扫描现有文件中...");

                var excelFiles = await FindAllExcelFilesAsync(_config.RootMonitorPath);

                _logger?.LogInformation("找到 {FileCount} 个现有Excel文件", excelFiles.Count);
                StatusChanged?.Invoke(this, $"找到 {excelFiles.Count} 个现有Excel文件");

                // 批量处理现有文件
                var tasks = excelFiles.Select(file => ProcessFileDetectionAsync(file, isExistingFile: true));
                await Task.WhenAll(tasks);

                _logger?.LogInformation("现有文件扫描完成");
                StatusChanged?.Invoke(this, "现有文件扫描完成");
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "扫描现有文件时发生错误");
                OnErrorOccurred(ex);
            }
        }

        private void InitializeFileWatcher()
        {
            _fileWatcher = new FileSystemWatcher(_config.RootMonitorPath)
            {
                IncludeSubdirectories = true,
                Filter = _config.GetFileFilter(),
                NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime,
                InternalBufferSize = _config.BufferSize
            };

            _fileWatcher.Created += OnFileCreated;
            _fileWatcher.Changed += OnFileChanged;
            _fileWatcher.Deleted += OnFileDeleted;
            _fileWatcher.Renamed += OnFileRenamed;
            _fileWatcher.Error += OnFileWatcherError;
        }

        private void OnFileCreated(object sender, FileSystemEventArgs e)
        {
            if (!_isWatching) return;
            if (!IsExcelFile(e.FullPath)) return;

            _logger?.LogDebug("检测到文件创建: {FileName}", e.Name);
            ScheduleFileProcessing(e.FullPath, FileEventType.Created);
        }

        private void OnFileChanged(object sender, FileSystemEventArgs e)
        {
            if (!_isWatching) return;
            if (!IsExcelFile(e.FullPath)) return;

            _logger?.LogDebug("检测到文件变化: {FileName}", e.Name);
            ScheduleFileProcessing(e.FullPath, FileEventType.Modified);
        }

        private void OnFileDeleted(object sender, FileSystemEventArgs e)
        {
            if (!_isWatching) return;
            if (!IsExcelFile(e.FullPath)) return;

            _logger?.LogDebug("检测到文件删除: {FileName}", e.Name);
            ProcessFileDeletion(e.FullPath);
        }

        private void OnFileRenamed(object sender, RenamedEventArgs e)
        {
            if (!_isWatching) return;
            if (!IsExcelFile(e.FullPath)) return;

            _logger?.LogDebug("检测到文件重命名: {OldName} -> {NewName}", e.OldName, e.Name);

            // 处理旧文件删除
            ProcessFileDeletion(e.OldFullPath);
            // 处理新文件创建
            ScheduleFileProcessing(e.FullPath, FileEventType.Created);
        }

        private void OnFileWatcherError(object sender, ErrorEventArgs e)
        {
            var ex = e.GetException();
            _logger?.LogError(ex, "文件监控错误");
            OnErrorOccurred(ex);
        }

        private void ScheduleFileProcessing(string filePath, FileEventType eventType)
        {
            _pendingChanges.AddOrUpdate(filePath, DateTime.Now, (k, v) => DateTime.Now);
            _debounceTimer.Change(_config.DebounceIntervalMs, Timeout.Infinite);
        }

        private void DebounceTimerCallback(object state)
        {
            if (!_isWatching) return;

            var now = DateTime.Now;
            var filesToProcess = _pendingChanges
                .Where(x => (now - x.Value).TotalMilliseconds >= _config.DebounceIntervalMs - 100)
                .Select(x => x.Key)
                .ToList();

            foreach (var filePath in filesToProcess)
            {
                if (_pendingChanges.TryRemove(filePath, out _))
                {
                    _ = ProcessFileDetectionAsync(filePath, FileEventType.Created);
                }
            }
        }

        private async Task ProcessFileDetectionAsync(string filePath, FileEventType eventType = FileEventType.Created, bool isExistingFile = false)
        {
            if (!_isWatching) return;

            Interlocked.Increment(ref _activeProcesses);

            try
            {
                await _processingSemaphore.WaitAsync(_cancellationTokenSource.Token);

                try
                {
                    await Task.Run(async () =>
                    {
                        await ProcessFileInternalAsync(filePath, eventType, isExistingFile);
                    }, _cancellationTokenSource.Token);
                }
                finally
                {
                    _processingSemaphore.Release();
                }
            }
            catch (OperationCanceledException)
            {
                _logger?.LogWarning("文件处理被取消: {FilePath}", filePath);
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "处理文件时发生错误: {FilePath}", filePath);
                OnErrorOccurred(ex);
            }
            finally
            {
                Interlocked.Decrement(ref _activeProcesses);
                if (!isExistingFile)
                {
                    Interlocked.Increment(ref _totalFilesDetected);
                }
            }
        }

        private async Task ProcessFileInternalAsync(string filePath, FileEventType eventType, bool isExistingFile)
        {
            try
            {
                // 验证文件可访问性
                var healthCheck = await CheckFileHealthAsync(filePath);

                if (!healthCheck.IsAccessible || !healthCheck.IsExcelFile)
                {
                    _logger?.LogWarning("文件不可访问或不是Excel文件: {FilePath}", filePath);
                    return;
                }

                // 检查是否是新文件或需要报告的文件
                if (ShouldReportFile(filePath, isExistingFile))
                {
                    var fileEvent = new ExcelFileEvent(filePath, eventType)
                    {
                        FileSize = healthCheck.FileSize,
                        LastWriteTime = healthCheck.LastModified
                    };

                    _fileLastSeen.AddOrUpdate(filePath, DateTime.Now, (k, v) => DateTime.Now);

                    OnFileDetected(fileEvent);
                    _logger?.LogInformation("检测到Excel文件: {FilePath} (事件类型: {EventType})", filePath, eventType);
                }
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "处理文件内部错误: {FilePath}", filePath);

                var errorEvent = new ExcelFileEvent(filePath, FileEventType.Error)
                {
                    Error = ex
                };

                OnFileDetected(errorEvent);
            }
        }

        private void ProcessFileDeletion(string filePath)
        {
            try
            {
                _fileLastSeen.TryRemove(filePath, out _);
                _pendingChanges.TryRemove(filePath, out _);

                var deleteEvent = new ExcelFileEvent(filePath, FileEventType.Deleted);
                OnFileDetected(deleteEvent);

                _logger?.LogInformation("文件被删除: {FilePath}", filePath);
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "处理文件删除时发生错误: {FilePath}", filePath);
                OnErrorOccurred(ex);
            }
        }

        private bool ShouldReportFile(string filePath, bool isExistingFile)
        {
            // 对于现有文件，只在首次扫描时报告
            if (isExistingFile)
                return true;

            // 对于新文件，总是报告
            // 可以添加更复杂的逻辑，比如检查文件是否已经处理过等
            return true;
        }

        private bool IsExcelFile(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
                return false;

            var extension = Path.GetExtension(filePath).ToLower();
            return _config.FileExtensions.Contains(extension);
        }

        private async Task<List<string>> FindAllExcelFilesAsync(string directoryPath)
        {
            var excelFiles = new List<string>();

            try
            {
                // 搜索当前目录
                foreach (var extension in _config.FileExtensions)
                {
                    var files = Directory.GetFiles(directoryPath, $"*{extension}", SearchOption.TopDirectoryOnly);
                    excelFiles.AddRange(files);
                }

                // 递归搜索子目录
                var subDirectories = Directory.GetDirectories(directoryPath);
                var subDirectoryTasks = subDirectories.Select(dir => FindAllExcelFilesAsync(dir));
                var subDirectoryResults = await Task.WhenAll(subDirectoryTasks);

                excelFiles.AddRange(subDirectoryResults.SelectMany(files => files));
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "搜索目录时发生错误: {Directory}", directoryPath);
            }

            return excelFiles;
        }

        public async Task<bool> VerifyFileAccessAsync(string filePath)
        {
            try
            {
                return await _fileAccessHelper.ExecuteWithRetryAsync(() =>
                {
                    using var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                    return Task.FromResult(true);
                }, filePath);
            }
            catch
            {
                return false;
            }
        }

        public async Task<FileHealthCheck> CheckFileHealthAsync(string filePath)
        {
            var healthCheck = new FileHealthCheck
            {
                FilePath = filePath,
                IsExcelFile = IsExcelFile(filePath)
            };

            var startTime = DateTime.Now;

            try
            {
                healthCheck.IsAccessible = await VerifyFileAccessAsync(filePath);

                if (healthCheck.IsAccessible)
                {
                    var fileInfo = new FileInfo(filePath);
                    healthCheck.FileSize = fileInfo.Length;
                    healthCheck.LastModified = fileInfo.LastWriteTime;
                }
            }
            catch (Exception ex)
            {
                healthCheck.IsAccessible = false;
                healthCheck.Error = ex;
            }

            healthCheck.CheckDuration = DateTime.Now - startTime;

            return healthCheck;
        }

        protected virtual void OnFileDetected(ExcelFileEvent e)
        {
            try
            {
                FileDetected?.Invoke(this, e);
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "处理文件检测事件时发生错误");
                OnErrorOccurred(ex);
            }
        }

        protected virtual void OnErrorOccurred(Exception ex)
        {
            try
            {
                ErrorOccurred?.Invoke(this, ex);
            }
            catch (Exception eventEx)
            {
                _logger?.LogError(eventEx, "处理错误事件时发生错误");
            }
        }

        public void Dispose()
        {
            try
            {
                StopWatchingAsync().GetAwaiter().GetResult();
                _cancellationTokenSource?.Cancel();
                _fileWatcher?.Dispose();
                _debounceTimer?.Dispose();
                _processingSemaphore?.Dispose();
                _cancellationTokenSource?.Dispose();

                _logger?.LogInformation("Excel文件监控资源已释放");
                StatusChanged?.Invoke(this, "监控资源已释放");
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "释放资源时发生错误");
            }
        }
    }
}
